"""
    SQLObject.column inheritors and their validators.
"""

import types
from decimal import Decimal

from formencode import validators
from sqlobject import constraints, sqlbuilder
from sqlobject.col import SOCol, SOIntCol, Col, SOUnicodeCol

from mnemoply.utils import make_enum, PhoneFormat

# Decimal
class DecimalValidator(validators.Validator):
    def to_python(self, value, state):
        if value is None:
            return None
        if isinstance(value, (int, long, Decimal, sqlbuilder.SQLExpression)):
            return value
        if isinstance(value, float):
            value = str(value)
        connection = state.soObject._connection
        if hasattr(connection, "decimalSeparator"):
            value = value.replace(connection.decimalSeparator, ".")
        try:
            return Decimal(value)
        except:
            raise validators.Invalid("expected a Decimal in the DecimalCol '%s', got %s %r instead" % \
                (self.name, type(value), value), value, state)

    def from_python(self, value, state):
        if value is None or value is '':
            return None
        if isinstance(value, float):
            value = str(value)
        if isinstance(value, (str, unicode)):
            connection = state.soObject._connection
            if hasattr(connection, "decimalSeparator"):
                value = value.replace(connection.decimalSeparator, ".")
            if value.count(","):
                value = value.replace(",", ".")
            try:
                return Decimal(value)
            except:
                raise validators.Invalid("can not parse Decimal value '%s' in the DecimalCol from '%s'" %
                    (value, getattr(state, 'soObject', '(unknown)')), value, state)
        if not isinstance(value, (int, long, Decimal, sqlbuilder.SQLExpression)):
            raise validators.Invalid("expected a decimal in the DecimalCol '%s', got %s %r instead" % \
                (self.name, type(value), value), value, state)
        return value

class SODecimalCol(SOCol):

    def __init__(self, **kw):
        self.size = kw['size']
        self.precision = kw['precision']

        del kw['size']
        del kw['precision']

        super(SODecimalCol, self).__init__(**kw)

    def _sqlType(self):
        return 'DECIMAL(%i, %i)' % (self.size, self.precision)

    def createValidators(self):
        return [DecimalValidator()] + \
            super(SODecimalCol, self).createValidators()

class DecimalCol(Col):
    baseClass = SODecimalCol

# Currency
class SOCurrencyCol(SODecimalCol):

    def __init__(self, **kw):
        if not kw.has_key('size'):
            kw['size'] = 10
        if not kw.has_key('precision'):
            kw['precision'] = 2

        super(SOCurrencyCol, self).__init__(**kw)

class CurrencyCol(DecimalCol):
    baseClass = SOCurrencyCol

# Discont
class DiscontValidator(validators.Validator):
    def from_python(self, value, state=None):
        if value is None:
            return None

        if type(value) == types.StringType or type(value) == types.UnicodeType:
            value = self.to_python(value)

        v = []
        for x in range(0, 12):
            if value[x] is None:
                v.append("")
            else:
                v.append(str(value[x]))
        return ":".join(v)

    def to_python(self, value, state=None):
        def str2int(s):
            if not s:
                return None
            else:
                return int(s)

        if value is None:
            return None
        v = [str2int(x) for x in value.split(":")]
        assert len(v) == 12
        return v

def is_discont(v):
    assert len(v) == 12
    for x in range(0, 12):
        constraints.isInt(v[x])

class SODiscontCol(SOCol):
    def autoConstraints(self):
        return [is_discont]

    def createValidators(self):
        return [DiscontValidator()] + super(SODiscontCol, self).createValidators()

    def _sqlType(self):
        return 'TEXT'

class DiscontCol(Col):
    baseClass = SODiscontCol

# Tariff
class TariffValidator(validators.Validator):
    def from_python(self, value, state=None):
        if value is None:
            return None

        if type(value) == types.StringType or type(value) == types.UnicodeType:
            value = self.to_python(value)

        v = [(value[x] is not None) and str(value[x]) or '' for x in range(0, 12)]
        return ":".join(v)

    def to_python(self, value, state=None):
        def str2decimal(s):
            if not s:
                return None
            else:
                return Decimal(s)

        if value is None:
            return None
        v = [str2decimal(x) for x in value.split(":")]
        assert len(v) == 12
        return v

def is_tariff(v):
    assert len(v) == 12
    for x in range(0, 12):
        constraints.isFloat(v[x])

class SOTariffCol(SOCol):
    def autoConstraints(self):
        return [is_tariff]

    def createValidators(self):
        return [TariffValidator()] + \
            super(SOTariffCol, self).createValidators()

    def _sqlType(self):
        return 'TEXT'

class TariffCol(Col):
    baseClass = SOTariffCol

# DaysCapability
DayCapability = make_enum('IMPOSSIBLE', 'NEUTRALLY', 'DESIRABLY', 'FIT')

class DaysCapabilityValidator(validators.Validator):
    def from_python(self, value, state=None):
        if isinstance(value, (str, unicode)):
            value = self.to_python(value)

        if value is None:
            return None

        result = ''
        for x in range(0, 7):
            v = '%d' % value[x].value
            assert len(v) == 1
            result += v
        return result

    def to_python(self, value, state=None):
        if value is None:
            return None

        assert isinstance(value, (str, unicode))

        if not len(value):
            return None

        assert len(value) == 7

        v = [DayCapability[int(x)] for x in value]
        return v

def is_days_capability(v):
    assert len(v) == 7
    for x in range(0, 7):
        if not v[x] in DayCapability:
            raise ValueError("Invalid enum item: " + str(v[x]))

class SODaysCapabilityCol(SOCol):
    def autoConstraints(self):
        return [is_days_capability]

    def createValidators(self):
        return [DaysCapabilityValidator()] + \
            super(SODaysCapabilityCol, self).createValidators()

    def _sqlType(self):
        return 'TEXT'

class DaysCapabilityCol(Col):
    baseClass = SODaysCapabilityCol

# Gender
Gender = make_enum('MALE', 'FEMALE')

class GenderValidator(validators.Validator):
    def from_python(self, value, state=None):
        if value is None:
            return None

        if type(value) == types.StringType or type(value) == types.UnicodeType:
            value = self.to_python(value)

        if value == Gender.MALE:
            return 0
        elif value == Gender.FEMALE:
            return 1
        else:
            raise validators.Invalid("Invalid Gender field")

    def to_python(self, value, state=None):
        if value is None:
            return None
        if not len(str(value)):
            return None

        if str(value).lower() == 'male':
            return Gender.MALE
        elif str(value).lower() == 'female':
            return Gender.FEMALE

        value = int(value)
        if not value:
            return Gender.MALE
        elif value == 1:
            return Gender.FEMALE
        else:
            raise validators.Invalid("Invalid Gender field")

class SOGenderCol(SOIntCol):
    def createValidators(self):
        return [GenderValidator()] + \
            super(SOIntCol, self).createValidators()

class GenderCol(Col):
    baseClass = SOGenderCol


class PhoneValidator(validators.Validator):
    def from_python(self, value, state=None):
        return value
  
    def to_python(self, value, state=None):
        return value
    
class SOPhoneCol(SOCol):
    def createValidators(self):
        return [PhoneValidator()] +super(SOPhoneCol, self).createValidators()

    def _sqlType(self):
        return 'TEXT'

class PhoneCol(Col):
    baseClass = SOPhoneCol